/*The MIT License

Copyright (c) <1/27/2011> <erick.difiore>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/

package com.d4a5.mapping.core
{
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	
	public class Utils
	{
		protected static const EARTH_RADIUS:Number = 6378137.0;
		protected static const PI_OVER_180:Number = 0.017453292519943295769236907684;
		protected static const PI_OVER_360:Number = 0.0087266462599716478846184538424417;
		protected static const _360_OVER_PI:Number = 114.59155902616464175359630963403;
		protected static const PI_OVER_4:Number = 0.78539816339744830961566084581975;
		protected static const PI_OVER_180_TIMES_EARTH_RADIUS:Number = 111319.490793273567741415;
		protected static const EARTH_RADIUS_OVER_2:Number = 3189068.5;
		protected static const ONE_OVER_90:Number = 0.011111111111111111111111111111111;
		
		protected static const SIZE_OF_EARTH:Number = 40075016.685578494;
		
		protected static const ONEANDHALF_SIZE_OF_EARTH:Number = 60112525.028367741;
		protected static const ONEANDHALF_TILE_SIZE:Number = 1878516.40713649190625;
		
		protected static const HALFSIZE_OF_EARTH:Number = 20037508.342789247;
		protected static const HALF_TILE_SIZE:Number = 1252344.2714243279375;
		
		
		
		public static function WebMercatorToGeographic( mercatorX:Number , mercatorY:Number  ) : Point {
			return new Point( mercatorX / PI_OVER_180_TIMES_EARTH_RADIUS , 90 -  (_360_OVER_PI * Math.atan( Math.exp( -mercatorY / EARTH_RADIUS ) ) ) );
		}
		
		public static function WebMercatorToGeographicFaster( mercatorX:Number , mercatorY:Number  , output:Point ) : void {
			output.x =  mercatorX / PI_OVER_180_TIMES_EARTH_RADIUS;
			output.y = 90 -  (_360_OVER_PI * Math.atan( Math.exp( -mercatorY / EARTH_RADIUS ) ) );
		}
		
		public static function GeographicToWebMercator( lat:Number , lon:Number  ) : Point {
			return new Point( lon * PI_OVER_180_TIMES_EARTH_RADIUS  , EARTH_RADIUS * Math.log( Math.tan( lat*PI_OVER_360+PI_OVER_4 ) ) );
		}
		public static function GeographicToWebMercatorFaster( lat:Number , lon:Number , output:Point ) : void {
			output.x = lon * PI_OVER_180_TIMES_EARTH_RADIUS;
			output.y = EARTH_RADIUS * Math.log( Math.tan( lat*PI_OVER_360+PI_OVER_4 ) );
		}
		
		public static function createXYSpatialIndex( x:Number , y:Number ) : Object {
			
			return { x : 0x1 << uint((x + HALFSIZE_OF_EARTH) / HALF_TILE_SIZE) , y : 0x1 << uint((y + HALFSIZE_OF_EARTH) / HALF_TILE_SIZE) };
		}
		
		public static function createMinMaxSpatialIndex( min:Number , max:Number ) : uint {
			var minTile:uint = uint((min + HALFSIZE_OF_EARTH) / HALF_TILE_SIZE); 
			var maxTile:uint = uint((max + HALFSIZE_OF_EARTH) / HALF_TILE_SIZE)+1; 
			
			return ((0xffffffff << minTile) ^ (0xffffffff<< maxTile));
		}
		
		
		public static function reallyHideESRILogo( map:Object ) : void {
			map.logoVisible = false;
			for(var i : int = 0 ; i < map.numChildren ; i++){
				var component:Object = map.getChildAt(i);
				if(component.className == "StaticLayer"){
					for(var j : int = 0 ; j < component.numChildren ; j++){
						var stComponent:Object = component.getChildAt(j);
						if(stComponent.className == "Image"){
							stComponent.visible = false;
							return;
						}
					}
				}
			}
		}
		
		public static function calcValue( start:Number , stop:Number , pct:Number ) : Number {
			return ((stop-start)*pct)+start;
		}
		public static function calcColor( start:uint , stop:uint , pct:Number ) : uint {
			pct = (pct < 0) ? 0 : (( pct > 1 ) ? 1 : pct);
			
			//blue
			var a:uint = start & 0x0000ff;
			var b:uint = stop & 0x0000ff;
			var rgb:uint = ((b-a) * pct)+a;
			
			//green
			a = (start & 0x00ff00) >> 8;
			b = (stop & 0x00ff00) >> 8;
			rgb += (((b-a) * pct)+a) << 8;
			
			//red
			a = (start & 0xff0000) >> 16;
			b = (stop & 0xff0000) >> 16;
			rgb += (((b-a) * pct)+a) << 16;
			
			return rgb;
		}
		
		public static function getCompileDate( app:Object ) : Date {
			var bytes:ByteArray =  app.loaderInfo.bytes; 
			bytes.position = 0;
			var signatureByte1:uint = bytes.readUnsignedByte();
			var signatureByte2:uint = bytes.readUnsignedByte();
			var signatureByte3:uint = bytes.readUnsignedByte();
			var version:uint = bytes.readUnsignedByte();
			var fileLength:uint = bytes.readUnsignedInt();
			
			if( signatureByte1 == 0x46 && signatureByte2 == 0x57 && signatureByte3 == 0x53 ){
				//data is uncompressed
				//file length should equal filelength
			}
			else if( signatureByte1 == 0x43 && signatureByte2 == 0x57 && signatureByte3 == 0x53 ){
				//data is compressed from here on out
				//decompressed file length should equal filelength
			}
			
			var preRectPosition:uint = bytes.position; 
			var Nbits:uint = bytes.readUnsignedByte() >> 3;
			var rectSize:uint = Math.ceil( ( 5 + (Nbits*4) ) / 8 );
			
			bytes.position = preRectPosition + rectSize;
			
			var frameRate:uint = bytes.readUnsignedShort();
			var frameCount:uint = bytes.readUnsignedShort();
			
			bytes.endian = Endian.LITTLE_ENDIAN;
			var tags:Object = new Object();
			while ( bytes.bytesAvailable ){
				var tagCodeAndLength:uint = bytes.readUnsignedShort();
				var tagCode:uint = tagCodeAndLength >> 6;
				var tagLength:uint = tagCodeAndLength & 0x3f;
				tagLength = ( tagLength == 0x3f ) ?  bytes.readUnsignedInt() : tagLength;
				
				if( tagLength > 0 ){
					var tagInfo:ByteArray = new ByteArray();
					bytes.readBytes( tagInfo , 0 , tagLength );
					tags[ tagCode ] = tagInfo;
					
					if( tags[ 0x29 ] ){
						var compiledOn:Date = new Date();
						var tag:ByteArray = tags[ 0x29 ] as ByteArray;
						tag.position = 18;  
						tag.endian = Endian.LITTLE_ENDIAN;  
						compiledOn.time = tag.readUnsignedInt() + tag.readUnsignedInt() * (uint.MAX_VALUE + 1);  
						return compiledOn;
					}
				}
			}
			
			return null;  
		}
		
		
		
	}
}